#pragma once

#include <iostream>
#include <mutex>

#include <cassert>
#include <fstream>
#include <algorithm>
#include <jsoncpp/json/json.h>
#include "../Comm/Util.hpp"
#include "../Comm/Log.hpp"
#include "oj_model.hpp"
// #include "oj_model2.hpp"
#include "oj_view.hpp"
#include "../Comm/httplib.h"
namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_util;
    using namespace ns_log;
    using namespace httplib;

    class Machine // 后端服务器的信息
    {
    public:
        string _ip;
        int _port;
        uint64_t _load;
        mutex *_mtx; // 保护_load资源
    public:
        Machine() : _ip(""), _port(0), _load(0), _mtx(nullptr)
        {
        }

        Machine(const string &ip, int port) : _ip(ip), _port(port), _load(0), _mtx(new mutex)
        {
        }

        ~Machine()
        {
        }

    public:
        void IncLoad()
        {
            lock_guard<mutex> lock(*_mtx);
            ++_load;
        }
        void DecLoad()
        {
            lock_guard<mutex> lock(*_mtx);
            --_load;
        }
        void ClearLoad()
        {
            lock_guard<mutex> lock(*_mtx);
            _load = 0;
        }
    };

    const string service_machine = "./Conf/server_machine.conf";
    class LoadBalance
    {
    private:
        vector<Machine> _machines; // 服务器
        vector<int> _online;       // 在线服务器下标
        vector<int> _offline;      // 离线服务器下标
        mutex _mtx;

    public:
        LoadBalance()
        {
            assert(LoadMachine(service_machine));
            // LoadMachine(service_machine);
        }
        ~LoadBalance()
        {
        }

        bool LoadMachine(const string &path)
        {
            // 读取配置文件中的主机
            ifstream in(path);
            if (!in.is_open())
            {
                LOG(FATAL) << "加载 " << path << " 失败\n";
                return false;
            }
            string line;
            while (getline(in, line))
            {
                vector<string> v;
                String_Util::SplitString(line, &v, ":");
                if (v.size() != 2)
                {
                    LOG(WARNING) << "分割 " << line << " 失败\n";
                    continue;
                }
                _online.emplace_back(_machines.size());
                _machines.emplace_back(v[0], stoi(v[1]));
                LOG(INFO) << "加载" << line << "服务器成功\n";
            }
            LOG(INFO)<< "加载服务器成功\n";
            return true;
        }

        bool SmartChoice(int *id, Machine **m)
        {
            lock_guard<mutex> lock(_mtx);
            std::cout << "Online size : " << _online.size() << " 分别为 ";
            for (auto x : _online)
            {
                std::cout << x << " ";
            }
            std::cout << "\n";

            std::cout << "Offline size : " << _offline.size() << " 分别为 ";
            for (auto x : _offline)
            {
                std::cout << x << " ";
            }
            std::cout << "\n";

            if (_online.empty())
            {
                return false;
            }

            int load = _machines[_online[0]]._load;
            *id = _online[0];
            *m = &_machines[_online[0]];

            for (int i : _online)
            {
                if (_machines[i]._load < load)
                {
                    load = _machines[i]._load;
                    *id = i;
                    *m = &_machines[i];
                }
            }
            return true;
        }
        void OfflineMachine(int idx)
        {
            lock_guard<mutex> lock(_mtx);
            for (auto it = _online.begin(); it != _online.end(); ++it)
            {
                if (*it == idx)
                {
                    _machines[idx].ClearLoad();
                    _offline.emplace_back(*it);
                    _online.erase(it);
                    break;
                }
            }
        }
        void OnlineMachine()
        {
            lock_guard<mutex> lock(_mtx);
            _online.insert(_online.end(), _offline.begin(), _offline.end());
            _offline.erase(_offline.begin(), _offline.end());
        }
    };

    class Control
    {
    private:
        Model _model;
        View _view;
        LoadBalance _lb;

    public:
        Control() {}
        ~Control() {}
        void Recovery()
        {
            _lb.OnlineMachine();
        }
        bool AllQuestions(string *html)
        {
            vector<struct Question> out;
            if (_model.GetAllQuestions(&out))
            {
                // 获取题目成功
                LOG(INFO) << " 获取题目列表成功 \n";
                sort(out.begin(), out.end(), [&](const struct Question &x, const struct Question &y)
                     { return stoi(x.number) < stoi(y.number); });
                _view.AllExpandHtml(out, html);
                return true;
            }
            else
            {
                *html = "获取题目列表失败";
                return false;
            }
        }

        bool Question(const string &number, string *html)
        {
            struct Question q;
            LOG(INFO) << "获取一个完整的题目\n";
            if (_model.GetOneQuestion(number, &q))
            {
                // 获取题目成功
                _view.OneExpandHtml(q, html);
                return true;
            }
            else
            {
                *html = "获取题目失败";
                return false;
            }
        }

        void Judge(const string &number, const string &in_json, string *out_json)
        {
            // 0、根据编号拿到题目信息
            struct Question q;
            _model.GetOneQuestion(number, &q);

            // 1、将in_json反序列化得到code信息
            Json::Value in;
            Json::Reader reader;
            reader.parse(in_json, in);

            // 2、将得到的code和测试样例结合上交给Compile
            Json::Value compile_value;
            compile_value["input"] = in["input"].asString();
            compile_value["code"] = in["code"].asString() +"\n"+ q.tail;
            compile_value["cup_limit"] = q.cpu_limit;
            compile_value["memo_limit"] = q.memo_limit;

            Json::StyledWriter writer;
            // Json::FastWriter writer;
            string compile_string = writer.write(compile_value);

            // 3、负载均衡的将请求
            while (true)
            {
                int id = -1;
                Machine *m = nullptr;
                if (!_lb.SmartChoice(&id, &m))
                {
                    LOG(FATAL) << "没有可用的后端服务器，请运维的哥们排查下原因\n";
                    break;
                }
                LOG(INFO) << "选择的服务器成功，相关信息为" << m->_ip << ":" << m->_port << endl;
                // 4、交给后端的服务器
                Client client(m->_ip, m->_port);
                m->IncLoad();
                if (auto res = client.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {

                    if (res->status == 200)
                    {
                        // 5、将返回结果交给out_json
                        *out_json = res->body;
                        LOG(INFO) << "服务器成功，相关信息为" << m->_ip << ":" << m->_port << endl;
                        m->DecLoad();
                        break;
                    }
                    m->DecLoad();
                }
                else
                {
                    LOG(ERROR) << "申请的" << m->_ip << ":" << m->_port << " 服务器可能已经离线\n";
                    _lb.OfflineMachine(id);
                }
            }
        }
    };
}